﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using Interpreter;

namespace LogoScriptIDE
{
    class FuncPrint
       : IFunction
    {
        public FuncPrint(ConsoleWindow cw)
        {
            m_cw = cw;
        }
        private delegate void Delegate(string s);
        public Variable invoke(Machine m, int numParams) //Note that the interface has changed
        {
            Delegate func = new Delegate(m_cw.Write);
            Variable v;
            string str = "";
            for (int i = 1; i <= numParams; ++i) //param have ID from 1 to numParams(possibly 0)
            {
                v = m.GetLocalByID(i); //How to access each param, do not specify ID less than 1 or greater than numParams
                if (v.type == VarType.TYPE_NUMBER)
                {
                    str += v.num.ToString();
                }
                else if (v.type == VarType.TYPE_STRING)
                {
                    str += v.str;
                }
                else
                    throw new RuntimeException("Type mismatch"); //You can throw any runtime exception too.
            }
            str += "\n";
            m_cw.Dispatcher.Invoke(
                        func,
                        System.Windows.Threading.DispatcherPriority.ContextIdle,
                        str);
            return new Variable(0.0); //You can return any number here.
        }
        private ConsoleWindow m_cw;
    }

    class FuncPRINTR
        : IFunction
    {
        public FuncPRINTR(ConsoleWindow cw)
        {
            m_cw = cw;
            printFunc = new Delegate(m_cw.Write);
        }
        private delegate void Delegate(string s);
        private Delegate printFunc;
        private void printString(string str)
        {
            m_cw.Dispatcher.Invoke(printFunc,
                System.Windows.Threading.DispatcherPriority.ContextIdle,
                str);
        }
        private void printRecursive(Machine m,Variable var,HashSet<Variable> parents)
        {
            if (var.type == VarType.TYPE_TABLE)
            {
                if (parents.Contains(var))
                    printString("<Rcursive Table>");
                else
                {
                    printString("[\n");
                    parents.Add(var);
                    foreach (var item in var.table)
                    {
                        for (int i = 0; i < parents.Count; ++i)
                            printString("  ");
                        printString("[" + item.Key.ToString(m) + "] = ");
                        printRecursive(m, item.Value, parents);
                        printString(",\n");
                    }
                    parents.Remove(var);
                    for (int i = 0; i < parents.Count; ++i)
                        printString("  ");
                    printString("]");
                }
            }
            else printString(var.ToString(m));
        }
        public Variable invoke(Machine m, int numParams)
        {
            for (int i = 1; i <= numParams; ++i)
                printRecursive(m, m.GetLocalByID(i), 
                    new HashSet<Variable>(VarCompare.GetInstance())
                    );
            printString("\n");
            return new Variable(0.0);
        }
        private ConsoleWindow m_cw;
    }

    class FuncFD
        : IFunction
    {
        public FuncFD(Turtle t)
        {
            m_turtle = t;
        }
        public Variable invoke(Machine m, int numParams)
        {
            if (numParams < 1 || m.GetLocalByID(1).type != VarType.TYPE_NUMBER)
                throw new RuntimeException("Invaild arugment");

            m_turtle.MoveForward(m.GetLocalByID(1).num);

            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }

    class FuncBK
        : IFunction
    {
        public FuncBK(Turtle t)
        {
            m_turtle = t;
        }

        public Variable invoke(Machine m, int numParams)
        {
            if (numParams < 1 || m.GetLocalByID(1).type != VarType.TYPE_NUMBER)
                throw new RuntimeException("Invaild arugment");

            m_turtle.MoveForward(-m.GetLocalByID(1).num);

            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }

    class FuncLT
        : IFunction
    {
        public FuncLT(Turtle t)
        {
            m_turtle = t;
        }
        public Variable invoke(Machine m, int numParams)
        {
            if (numParams < 1 || m.GetLocalByID(1).type != VarType.TYPE_NUMBER)
                throw new RuntimeException("Invaild arugment");

            m_turtle.RotateClockwise(-m.GetLocalByID(1).num);

            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }

    class FuncRT
        : IFunction
    {
        public FuncRT(Turtle t)
        {
            m_turtle = t;
        }
        public Variable invoke(Machine m, int numParams)
        {
            if (numParams < 1 || m.GetLocalByID(1).type != VarType.TYPE_NUMBER)
                throw new RuntimeException("Invaild arugment");

            m_turtle.RotateClockwise(m.GetLocalByID(1).num);

            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }

    class FuncPU
        : IFunction
    {
        public FuncPU(Turtle t)
        {
            m_turtle = t;
        }
        public Variable invoke(Machine m, int numParams)
        {
            m_turtle.PenUp = true;
            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }

    class FuncPD
        : IFunction
    {
        public FuncPD(Turtle t)
        {
            m_turtle = t;
        }
        public Variable invoke(Machine m, int numParams)
        {
            m_turtle.PenUp = false;
            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }

    class FuncRESET
        : IFunction
    {
        public FuncRESET(Turtle t)
        {
            m_turtle = t;
        }
        private delegate void Delegate();
        public Variable invoke(Machine m, int numParams)
        {
            Delegate func = new Delegate(m_turtle.ResetAll);
            m_turtle.Dispatcher.Invoke(
                func,
                System.Windows.Threading.DispatcherPriority.ContextIdle);

            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }

    /// <summary>
    /// RESETA()
    /// 不改变海龟的位置，将海龟头直指正上方。
    /// </summary>
    class FuncRESETA
        : IFunction
    {
        public FuncRESETA(Turtle t)
        {
            m_turtle = t;
        }

        public Variable invoke(Machine m, int numParams)
        {
            //重置乌龟角度
            m_turtle.ResetAngle();
            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }

    /// <summary>
    /// SETSIZE(width,height)
    /// 设置画布的大小，长为width，宽为height，范围在0-2000
    /// </summary>
    class FuncSETSIZE
        : IFunction
    {
        public FuncSETSIZE(Turtle t)
        {
            m_turtle = t;
        }
        private delegate void Delegate(double w, double h);
        public Variable invoke(Machine m, int numParams)
        {
            // 确认两个参数为数字，范围为0-2000
            if (numParams < 2 ||
                m.GetLocalByID(1).type != VarType.TYPE_NUMBER ||
                m.GetLocalByID(2).type != VarType.TYPE_NUMBER)
            {
                throw new RuntimeException("Invaild arugment");
            }
            if ((m.GetLocalByID(1).num < 0) || (m.GetLocalByID(1).num > 2000) ||
                (m.GetLocalByID(2).num < 0) || (m.GetLocalByID(2).num > 2000))
                throw new RuntimeException("Illegal arugment");

            Delegate func = new Delegate(m_turtle.ResetCanvas);//乌龟类会调用到画布类来实现改变画布大小
            // 设置画布大小
            m_turtle.Dispatcher.Invoke(
                func,
                System.Windows.Threading.DispatcherPriority.ContextIdle,
                m.GetLocalByID(1).num, m.GetLocalByID(2).num);
            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }

    class FuncSETXY
        : IFunction
    {
        public FuncSETXY(Turtle t)
        {
            m_turtle = t;
        }

        public Variable invoke(Machine m, int numParams)
        {
            if (numParams < 2 ||
                m.GetLocalByID(1).type != VarType.TYPE_NUMBER ||
                m.GetLocalByID(2).type != VarType.TYPE_NUMBER)
            {
                throw new RuntimeException("Invaild arugment");
            }
            else
            {
                double x = m.GetLocalByID(1).num;
                double y = m.GetLocalByID(2).num;
                m_turtle.MoveTo(new Point(x, y));
            }
            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }

    /// <summary>
    /// DELAY(time)
    /// 延迟time毫秒
    /// </summary>
    class FuncDELAY
        : IFunction
    {
        public Variable invoke(Machine m, int numParams)
        {
            // 确认参数为数字
            if (numParams < 1 ||
                m.GetLocalByID(1).type != VarType.TYPE_NUMBER ||
                m.GetLocalByID(1).num < 0
                )
            {
                throw new RuntimeException("Invaild arugment");
            }
            // 进行系统延迟
            System.Threading.Thread.Sleep(Convert.ToInt32(m.GetLocalByID(1).num));
            return new Variable(0.0);
        }
    }

    /// <summary>
    /// SETPC(r,g,b)
    /// 设置海龟的落笔颜色，r, g, b分别是红色分量，绿色分量和蓝色分量，它们的值应属于0-255
    /// </summary>
    class FuncSETPC
    : IFunction
    {
        public FuncSETPC(Turtle t)
        {
            m_turtle = t;
        }

        public Variable invoke(Machine m, int numParams)
        {
            // 确认3个参数都为数字
            if (numParams < 3 ||
                m.GetLocalByID(1).type != VarType.TYPE_NUMBER ||
                m.GetLocalByID(2).type != VarType.TYPE_NUMBER ||
                m.GetLocalByID(3).type != VarType.TYPE_NUMBER)
            {
                throw new RuntimeException("Invaild arugment");
            }
            // 确认3个数字参数范围在规定范围内，即0-255
            if ((m.GetLocalByID(1).num < 0) || (m.GetLocalByID(1).num > 255) ||
                 (m.GetLocalByID(2).num < 0) || (m.GetLocalByID(2).num > 255) ||
                 (m.GetLocalByID(3).num < 0) || (m.GetLocalByID(1).num > 255))
                throw new RuntimeException("Illegal arugment");

            // 改变画笔颜色，3个参数分别是红色分量，绿色分量和蓝色分量
            m_turtle.SetPenColor(
                Convert.ToByte(m.GetLocalByID(1).num),
                Convert.ToByte(m.GetLocalByID(2).num),
                Convert.ToByte(m.GetLocalByID(3).num));

            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }

    class FuncSETBK
    : IFunction
    {
        public FuncSETBK(Turtle t)
        {
            m_turtle = t;
        }

        public Variable invoke(Machine m, int numParams)
        {
            if (numParams < 3 ||
                m.GetLocalByID(1).type != VarType.TYPE_NUMBER ||
                m.GetLocalByID(2).type != VarType.TYPE_NUMBER ||
                m.GetLocalByID(3).type != VarType.TYPE_NUMBER)
            {
                throw new RuntimeException("Invaild arugment");
            }

            if ((m.GetLocalByID(1).num < 0) || (m.GetLocalByID(1).num > 255) ||
                 (m.GetLocalByID(2).num < 0) || (m.GetLocalByID(2).num > 255) ||
                 (m.GetLocalByID(3).num < 0) || (m.GetLocalByID(1).num > 255))
                throw new RuntimeException("Illegal arugment");

            m_turtle.SetBackgroundColor(
                Convert.ToByte(m.GetLocalByID(1).num),
                Convert.ToByte(m.GetLocalByID(2).num),
                Convert.ToByte(m.GetLocalByID(3).num));

            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }


    /// <summary>
    /// SETPW(width)
    /// 设置画笔的宽度为width，wdith的范围属于0-100。
    /// </summary>
    class FuncSETPW
    : IFunction
    {
        public FuncSETPW(Turtle t)
        {
            m_turtle = t;
        }
        public Variable invoke(Machine m, int numParams)
        {
            // 确认读入的为数字且是正数，范围为0-100
            if (numParams < 1 ||
                m.GetLocalByID(1).type != VarType.TYPE_NUMBER ||
                m.GetLocalByID(1).num <= 0 || m.GetLocalByID(1).num >1000)
            {
                throw new RuntimeException("Invaild arugment");
            }
            // 设置画笔宽度
            m_turtle.SetPenWidth(m.GetLocalByID(1).num);

            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }

    /// <summary>
    /// SHOW()
    /// 若海龟当前隐藏，则将其显示
    /// </summary>
    class FuncSHOW
    : IFunction
    {
        public FuncSHOW(Turtle t)
        {
            m_turtle = t;
        }
        public Variable invoke(Machine m, int numParams)
        {
            // 设置乌龟出现
            m_turtle.Show = true;
            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }

    /// <summary>
    /// HIDE()
    /// 若当前海龟显示，则将其隐藏
    /// </summary>
    class FuncHIDE
    : IFunction
    {
        public FuncHIDE(Turtle t)
        {
            m_turtle = t;
        }

        public Variable invoke(Machine m, int numParams)
        {
            //设置乌龟消失
            m_turtle.Show = false;
            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }
    
    /// <summary>
    /// INPUT系统函数类, INPUT(string)
    /// 通过弹窗来实现用户数据给变量
    /// string为提示用户输入的提示语
    /// </summary>
    class FuncINPUT
    : IFunction
    {
        /// <summary>
        /// 获取当前海龟和主窗口参数
        /// </summary>
        /// <param name="t">海龟</param>
        /// <param name="parent">主窗口</param>
        public FuncINPUT(Turtle t, MainWindow parent)
        {
            m_turtle = t;
            m_inputboxWindow = new InputboxWindow();
            m_inputboxWindow.Owner = parent;
        }
        /// <summary>
        /// 函数指针
        /// </summary>
        /// <param name="s"></param>
        private delegate void Delegate(string s);

        /// <summary>
        /// 返回给虚拟机用户输入的结果
        /// </summary>
        private Variable Ans;

        /// <summary>
        /// 表示输入数据是否合法
        /// </summary>
        private bool isLegal;

        /// <summary>
        /// 表示是否有数据输入
        /// </summary>
        private bool inputDone;
        private InputboxWindow m_inputboxWindow;

        /// <summary>
        /// 通过inputboxWindow来提示用户输入数据，并获得次数据
        /// </summary>
        /// <param name="hint">给用户的输入提示语</param>
        private void inputNum(string hint)
        {
            isLegal = true;
            if (hint != "") m_inputboxWindow.ui_showText.Text = hint;
            else m_inputboxWindow.ui_showText.Text = "Please input a double:";
            m_inputboxWindow.ShowDialog();
            inputDone = (m_inputboxWindow.inputStatus() != 0);
            if (m_inputboxWindow.inputStatus() == 1)                    //表示读入为数字
                Ans = new Variable(m_inputboxWindow.getNum());          //读入数字
            else if (m_inputboxWindow.inputStatus() == 2)               //表示读入为字符串
                    Ans = new Variable(m_inputboxWindow.getString());   //读入字符串
            else if (m_inputboxWindow.inputStatus() == -1)
                    isLegal = false;
            else Ans = new Variable(0.0);
        }

        public Variable invoke(Machine m, int numParams)
        {
            Delegate func = new Delegate(inputNum);
            Variable v;
            string str = "";
            for (int i = 1; i <= numParams; ++i) //param have ID from 1 to numParams(possibly 0)
            {
                v = m.GetLocalByID(i); //How to access each param, do not specify ID less than 1 or greater than numParams
                if (v.type == VarType.TYPE_NUMBER)
                {
                    str += v.num.ToString();
                }
                else if (v.type == VarType.TYPE_STRING)
                {
                    str += v.str;
                }
                else
                    throw new RuntimeException("Type mismatch"); //You can throw any runtime exception too.
            }

            m_turtle.Dispatcher.Invoke(
                func,
                System.Windows.Threading.DispatcherPriority.ContextIdle,
                str);
            // 表示没有输入，显示相应提示
            if (!inputDone)
                throw new RuntimeException("Input cancelled");
            // 表示输入不合法，显示相应提示
            if (isLegal)
                return Ans;
            else
                throw new RuntimeException("Type mismatch");
        }
        private Turtle m_turtle;
    }

    class FuncTEXT
    : IFunction
    {
        public FuncTEXT(Turtle t)
        {
            m_turtle = t;
        }

        public Variable invoke(Machine m, int numParams)
        {
            if (numParams < 1 || m.GetLocalByID(1).type != VarType.TYPE_STRING)
                throw new RuntimeException("Invalid Argument");
            m_turtle.DrawText(m.GetLocalByID(1).str);
            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }

    class FuncUPDATE
    : IFunction
    {
        public FuncUPDATE(Turtle t)
        {
            m_turtle = t;
        }
        public Variable invoke(Machine m, int numParams)
        {
            Turtle.UpdateMode mode;
            if (numParams < 1 ||
                m.GetLocalByID(1).type != VarType.TYPE_NUMBER)
            {
                throw new RuntimeException("Invaild arugment");
            }

            switch (Convert.ToInt32(m.GetLocalByID(1).num))
            {
                case 1:
                    mode = Turtle.UpdateMode.UPDATE_AUTO;
                    break;
                case 2:
                    mode = Turtle.UpdateMode.UPDATE_MANUAL;
                    break;
                case 3:
                    mode = Turtle.UpdateMode.UPDATE_NOW;
                    break;
                default:
                    throw new RuntimeException("Invaild arugment");
            }
            m_turtle.Update(mode);
            return new Variable(0.0);
        }
        private Turtle m_turtle;
    }

    class FuncGETMOUSE
        : IFunction
    {
        private CanvasWindow m_cw;
        private delegate Point Delegate();
        public FuncGETMOUSE(CanvasWindow t)
        {
            m_cw = t;
        }

        public Variable invoke(Machine m, int numParams)
        {
            Delegate func = new Delegate(m_cw.GetMouseInput);
            Point p;
            
            p = (Point)m_cw.Dispatcher.Invoke(
                func,
                System.Windows.Threading.DispatcherPriority.ContextIdle
                );
            if (p.X < 0)
                return new Variable(0.0);
            else
            {
                if (numParams < 1 ||
                m.GetLocalByID(1).type != VarType.TYPE_TABLE)
                {
                    throw new RuntimeException("Invaild arugment");
                }
                m.GetLocalByID(1).table[new Variable("x")] = new Variable(p.X);
                m.GetLocalByID(1).table[new Variable("y")] = new Variable(p.Y);
                return new Variable(1.0);
            }
        }

    }
}
